<?php

namespace App\Common\Libs\Query;

use App\Common\Libs\Json;
use App\Consts\ExceptionCode;
use App\Exceptions\Error;
use Illuminate\Database\Eloquent\Concerns;
use Illuminate\Database\Eloquent\Relations\HasOne;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Traits\ForwardsCalls;

/**
 * 分表构造器优化
 * Trait SubQuery
 * @package App\Common\Libs\Query
 */
trait SubQuery
{
    use
        Concerns\HasEvents,
        Concerns\HasGlobalScopes,
        Concerns\HasRelationships,
        Concerns\HasTimestamps,
        Concerns\HidesAttributes,
        ForwardsCalls;

    /** @var array $fields */
    public $fields = [];

    /**
     * 获取关联模型实例
     * @param string $id
     * @param bool $attach
     * @return \Illuminate\Database\Eloquent\Builder
     */
    public static function getRelationInstance($id = '', $attach = false)
    {
        $model = self::getModel($id);
        \Illuminate\Support\Facades\DB::connection()->enableQueryLog();
        $query = self::getInstance($id, $attach);
        $tableName = self::tableName($id);
        return $query->where($model->primaryKey, '!=', 'RelationInstance-' . $tableName);
    }

    /**
     * get alias name
     * @param array $columnAry
     * @param string $alias
     * @return array
     */
    public static function getAliasName($columnAry, $alias = '')
    {
        if (empty($alias)) {
            $model = self::getModel();
            $alias = $model->getTable();
        }
        foreach ($columnAry as &$col) {
            $col = $alias . '.' . $col;
        }
        return $columnAry;
    }

    /**
     * Define a one-to-one relationship.
     * @param string $related
     * @param string|null $foreignKey
     * @param string|null $localKey
     * @param null $sid
     * @return \Illuminate\Database\Eloquent\Relations\HasOne
     */
    public function hasOne($related, $foreignKey = null, $localKey = null, $sid = 0)
    {
        if (!empty($sid)) {
            $instance = (new $related)::getModel($sid, true);
        } else {
            $instance = (new $related)->getModel();
        }
        $query = $instance->query($sid);
        $tableName = $instance->getTable();
        $foreignKey = $foreignKey ?: $this->getForeignKey();
        $localKey = $localKey ?: $this->getKeyName();
        return new HasOne($query, $this, $tableName.'.'.$foreignKey, $localKey);

        //return $this->newHasOne($query, $this, $tableName.'.'.$foreignKey, $localKey);
    }

    /**
     * Begin querying a model with eager loading.
     * @param array|string $relations
     * @param null $sid
     * @return \Illuminate\Database\Eloquent\Builder
     */
    public static function with($relations, $sid = null)
    {
        return static::getModel($sid)->with(
            is_string($relations) ? func_get_args() : $relations
        );
    }

    /**
     * 获取关联模型属性
     * @return mixed
     */
    public function getRelationAttributes()
    {
        $relation = \Illuminate\Support\Facades\DB::connection()->getQueryLog();
        $relationSql = [];
        $tableName = $this->table;
        foreach ($relation as $item) {
            if (in_array('RelationInstance-' . $tableName, $item['bindings'])) {
                $relationSql = $item;break;
            }
        }
        $sql = str_replace("?", "'%s'", $relationSql['query']);
        $sql = vsprintf($sql, $relationSql['bindings']);
        return \Illuminate\Support\Facades\DB::selectOne($sql);
    }

    /**
     * Save a new model and return the instance.
     * @example $model = SysMessageTemplate::getModel()->create(['title' => 'You have new message', 'content' => time()]);
     * @param  array  $attributes
     * @return \Illuminate\Database\Eloquent\Model|$this|mixed
     */
    public function create(array $attributes = [])
    {
        DB::beginTransaction();
        try {
            $primaryKey = $this->primaryKey;
            $tableName = $this->getTable();
            $columns = Schema::getColumnListing($tableName);
            foreach ($attributes as $key => $val) {
                if (!in_array($key, $columns)) unset($attributes[$key]);
            }

            $create = tap($this->newModelInstance($attributes), function ($instance) {
                $instance->save();
            });

            if (array_key_exists($primaryKey, $attributes)) {
                $create->id = $create->$primaryKey;
                $create->$primaryKey = $attributes[$primaryKey];
            }

            DB::commit();
        } catch (\Exception $e) {
            DB::rollBack();
            $message = '[CREATE]: ' . $e->getMessage();
            Log::channel('sql')->error($message . ' - data: ' . Json::encode(['attr' => $attributes]));
            return Error::handle('', ExceptionCode::CODE_FAIL, $message, true);
        }
        return $create;
    }

    /**
     * Update records in the database and return model.
     * @example $model = SysMessageTemplate::getModel()->where('id', 9)->first()->updateFormat(['title' => 'You have new message', 'content' => time()]);
     * @param  array  $attributes
     * @return int|mixed
     */
    public function updateFormat(array $attributes)
    {
        DB::beginTransaction();
        try {
            $this->fill($attributes)->save();
            DB::commit();
        } catch (\Exception $e) {
            DB::rollBack();
            $message = '[UPDATE_FORMAT]: '. $e->getMessage();
            Log::channel('sql')->error($message . ' - data: ' . Json::encode(['attr' => $attributes]));
            return Error::handle('', ExceptionCode::CODE_FAIL, $message, true);
        }
        return $this->fresh();
    }
}
